package com.lty.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.smallbun.screw.core.Configuration;
import cn.smallbun.screw.core.engine.EngineConfig;
import cn.smallbun.screw.core.engine.EngineTemplateType;
import cn.smallbun.screw.core.execute.DocumentationExecute;
import cn.smallbun.screw.core.process.ProcessConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.lty.code.bean.Entity;
import com.lty.code.bean.FieldInfo;
import com.lty.common.ErrorCode;
import com.lty.exception.BusinessException;
import com.lty.model.dto.code.GenerateRequest;
import com.lty.model.dto.code.SqlDocDTO;
import com.lty.model.entity.TemplateInfo;
import com.lty.model.enums.StatusEnum;
import com.lty.util.CodeUtil;
import com.lty.util.GrammarUtil;
import com.zaxxer.hikari.HikariDataSource;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import org.beetl.core.Template;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 *
 * @author lty
 */
@Service
public class CodeService {

    @Resource
    public TemplateService templateService;


    public Map<String, String> getAllCode(Entity entity, List<FieldInfo> fieldInfoList) throws IOException {
        Map<String, String> map = new HashMap<>();

        List<TemplateInfo> list = templateService.getList();
        for (TemplateInfo templateInfo : list) {
            Template template = CodeUtil.getTemplateByString(templateInfo.getTemplate());
            map.put(templateInfo.getCode(), CodeUtil.getTemplateResult(template, entity, fieldInfoList));
        }
        return map;
    }

    /**
     * 生成代码
     * @param request
     * @return
     * @throws IOException
     */
    public Map<String, String> generateCode(GenerateRequest request) throws IOException {
        Map<String, Object> binding = request.getBinding();
        List<String> templates = request.getTemplates();

        Map<String, String> result = new TreeMap<>();
        // 查询模板
        List<TemplateInfo> templateInfoList = templateService.list(new LambdaQueryWrapper<TemplateInfo>()
                .in(TemplateInfo::getCode, templates)
                .eq(TemplateInfo::getStatus, StatusEnum.ENABLE.getValue())
                .orderByAsc(TemplateInfo::getSortable));

        for (TemplateInfo templateInfo : templateInfoList) {
            Template template = CodeUtil.getTemplateByString(templateInfo.getTemplate());
            // 绑定数据
            for (Map.Entry<String, Object> entry : binding.entrySet()) {
                template.binding(entry.getKey(), entry.getValue());
            }
            result.put(templateInfo.getCode(), template.render());
        }
        return result;
    }

    public Map<String, Object> configBySql(String sql) throws Exception {
        Map<String, Object> result = new HashMap<>();
        List<FieldInfo> fieldInfos = new ArrayList<>();
        CreateTable createTable = (CreateTable) CCJSqlParserUtil.parse(sql);
        List<ColumnDefinition> columnDefinitions = createTable.getColumnDefinitions();

        String tableName = createTable.getTable().getName();
        List<String> createOptionsStrings = createTable.getTableOptionsStrings();
        result.put("description", GrammarUtil.getComment(createOptionsStrings));
        result.put("tableName", tableName);
        result.put("className", GrammarUtil.isFirstUpper(GrammarUtil.underline2Camel(tableName), true));
        result.put("classNameLowerCase", GrammarUtil.isFirstUpper(GrammarUtil.underline2Camel(tableName), false));

        for (ColumnDefinition columnDefinition : columnDefinitions) {
            FieldInfo fieldInfo = new FieldInfo();
            String columnName = columnDefinition.getColumnName();
            String dataType = columnDefinition.getColDataType().getDataType();
            List<String> columnSpecs = columnDefinition.getColumnSpecs();
            String columnComment = GrammarUtil.getComment(columnSpecs);
            String javaType = GrammarUtil.mapSqlTypeToJavaType(dataType);

            fieldInfo.setField(columnName);
            fieldInfo.setType(javaType);
            fieldInfo.setDescription(columnComment);
            fieldInfo.setQuery(true);
            fieldInfo.setAdd(true);
            fieldInfo.setUpdate(true);
            fieldInfos.add(fieldInfo);
        }
        result.put("fieldInfoList", fieldInfos);
        return result;
    }

    public Map<String, Object> configByEntity(String entityUrl) {
        // 根据路径得到反射类
        Class<?> clazz;
        try {
            clazz = Class.forName(entityUrl);
        } catch (ClassNotFoundException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "实体类路径错误");
        }
        Map<String, Object> result = new HashMap<>();
        List<FieldInfo> fieldInfos = new ArrayList<>();
        result.put("className", GrammarUtil.isFirstUpper(GrammarUtil.underline2Camel(clazz.getSimpleName()), true));
        result.put("classNameLowerCase", GrammarUtil.isFirstUpper(GrammarUtil.underline2Camel(clazz.getSimpleName()), false));
        result.put("tableName", GrammarUtil.camel2Underline(clazz.getSimpleName()));
        result.put("description", "");
        result.put("author", "lty");
        result.put("controllerPackage", "com.lty.controller");
        result.put("daoPackage", "com.lty.dao");
        result.put("serviceImplPackage", "com.lty.service.impl");
        result.put("servicePackage", "com.lty.service");
        result.put("primaryKeyType", "String");
        for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {
            FieldInfo fieldInfo = new FieldInfo();
            fieldInfo.setField(field.getName());
            fieldInfo.setType(field.getType().getSimpleName());
            fieldInfo.setDescription("");
            fieldInfo.setQuery(true);
            fieldInfo.setAdd(true);
            fieldInfo.setUpdate(true);
            fieldInfos.add(fieldInfo);
        }
        result.put("fieldInfoList", fieldInfos);
        return result;
    }

    public ByteArrayOutputStream generateAsMemoryStream(SqlDocDTO dto) throws Exception {
        // 创建临时目录
        File tempDir = Files.createTempDirectory("screw_temp").toFile();
        String tempOutputDir = tempDir.getAbsolutePath();

        // 生成文件配置
        EngineConfig engineConfig = EngineConfig.builder()
                // 生成文件路径，这里需要自己更换下路径
                .fileOutputDir(tempOutputDir)
                // 文件类型 HTML/WORD/MD 三种格式
                .fileType(dto.getFileType())
                // 生成模板实现
                .produceType(EngineTemplateType.freemarker).build();

        ProcessConfig processConfig = ProcessConfig.builder()
                // 根据名称指定表生成
                .designatedTableName(dto.getDesignatedTableName())
                // 根据表前缀生成
                .designatedTablePrefix(dto.getDesignatedTablePrefix())
                // 根据表后缀生成
                .designatedTableSuffix(dto.getDesignatedTableSuffix())
                .designatedTableSuffix(new ArrayList<>())
                // 忽略表名
                .ignoreTableName(dto.getIgnoreTableName())
                // 忽略表前缀
                .ignoreTablePrefix(dto.getIgnorePrefix())
                // 忽略表后缀
                .ignoreTableSuffix(dto.getIgnoreSuffix()).build();

        // 数据库名称（数据库用户连接）
        HikariDataSource hikariDataSource = new HikariDataSource();
        // 设置数据库连接
        // hikariDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true");
        String jdbcUrl = MessageFormat.format("jdbc:mysql://{0}:{1}/{2}?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true",
                dto.getHost(), dto.getPort().toString(), dto.getDbName());
        hikariDataSource.setJdbcUrl(jdbcUrl);
        hikariDataSource.setUsername(dto.getUserName());
        hikariDataSource.setPassword(dto.getPassword());
        // 生成文档配置（包含以下自定义版本号、描述等配置连接）
        Configuration config = Configuration.builder()
                .version(dto.getVersion())
                .description(dto.getDescription())
                .dataSource(hikariDataSource)
                .engineConfig(engineConfig)
                .produceConfig(processConfig)
                .build();
        // 执行生成
        new DocumentationExecute(config).execute();

        // 假设默认生成一个文件
        File[] files = new File(tempOutputDir).listFiles((dir, name) -> name.endsWith(dto.getFileType().getFileSuffix()));
        if (files == null || files.length == 0) {
            throw new RuntimeException("未生成文档");
        }
        File generatedFile = files[0];
        // 读取文件为 byte[]
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try (InputStream is = new FileInputStream(generatedFile)) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) > -1) {
                outputStream.write(buffer, 0, len);
            }
        }
        FileUtil.del(tempDir);
        // 此时 outputStream 即为文件的字节流,可以保存、传输等
        // System.out.println("文档大小：" + outputStream.size() + " bytes");
        return outputStream;
    }
}
